package com.xand.bt.market;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xand.bt.api.MarketApiConst;
import com.xand.bt.api.MarketImageChangeNotify;

public class MarketStore {

	private static final Logger logger = LoggerFactory.getLogger(MarketStore.class);
	
	private List<MarketLevel> bid;
	private Map<Long, MarketLevel> bidLevelsByPrice;

	private List<MarketLevel> ask;
	private Map<Long, MarketLevel> askLevelsByPrice;
	
	private String marketLabel;

	public MarketStore(String marketLabel) {

		this.bid = new ArrayList<MarketLevel>();
		this.bidLevelsByPrice = new HashMap<Long, MarketLevel>();

		this.ask = new ArrayList<MarketLevel>();
		this.askLevelsByPrice = new HashMap<Long, MarketLevel>();

		this.marketLabel = marketLabel;
		
	}

	public synchronized void notifyMarketImageChange(MarketImageChangeNotify change) {

		this.traceMarketChange(change);
		
		MarketLevel level = this.getLevelForChangeNotification(change);
		
		if(change.getTime() > level.getLastUpdateTime()) {
			
			long oldVolume = level.getVolume();
			long newVolume = oldVolume + change.getVolume();
			
			level.setVolume(newVolume);
			
			level.setLastUpdateTime(change.getTime());
			
			if(newVolume == 0) {
				// remove level
				this.removeMarketLevel(level);
			}else if(newVolume < 0) {
				
				String direction = "";
				if(change.getDirection() == MarketApiConst.DIRECTION_ASK)
					direction = "ASK";
				else
					direction = "BID";
				
				logger.error("Level (" + direction + ") for price " + level.getPrice() + " has negative volume!!!");
			}
			
		} else {
			
			Date lastUpdateTime = new Date(level.getLastUpdateTime());
			Date cTime = new Date(change.getTime());
			
			String sLastUpdateTime = MarketApiConst.TIME_ONLY_DATE_FORMAT.format(lastUpdateTime);
			String sCTime = MarketApiConst.TIME_ONLY_DATE_FORMAT.format(cTime);
			
			String direction = "";
			if(change.getDirection() == MarketApiConst.DIRECTION_ASK)
				direction = "ASK";
			else
				direction = "BID";
			
			logger.warn("Invalid market change of " + direction + " level for price " + change.getPrice() + " received, lastUpdateTime=" + sLastUpdateTime + "; currentUpdateTime=" + sCTime);
			
		}
		
	}
	
	private void traceMarketChange(MarketImageChangeNotify change) {
		
		Logger cLogger = LoggerFactory.getLogger(this.marketLabel + "_MARKET_CHANGES_LOGGER");
		cLogger.info(change.toString());
		
	}
	
	private void removeMarketLevel(MarketLevel level) {
		
		List<MarketLevel> list = null;
		Map<Long, MarketLevel> map = null;
		
		if(level.getDirection() == MarketApiConst.DIRECTION_ASK) {

			map = this.askLevelsByPrice;
			list = this.ask;
			
		}else{
			
			map = this.bidLevelsByPrice;
			list = this.bid;
		}
		
		map.remove(level.getPrice());
		list.remove(level);
		
	}
	
	private MarketLevel getLevelForChangeNotification(MarketImageChangeNotify change) {
		
		MarketLevel level = null;
		
		List<MarketLevel> list = null;
		Map<Long, MarketLevel> map = null;
		
		if(change.getDirection() == MarketApiConst.DIRECTION_ASK) {

			map = this.askLevelsByPrice;
			list = this.ask;
			
		}else{
			
			map = this.bidLevelsByPrice;
			list = this.bid;
		}
		
		level = map.get(change.getPrice());
		
		if(level == null) {
			
			level = new MarketLevel();
			level.setPrice(change.getPrice());
			level.setDirection(change.getDirection());
			level.setMarketLabel(change.getMarketLabel());
			
			map.put(level.getPrice(), level);
			
			list.add(level);
			
			Collections.sort(list);
		}
		
		return level;
				
	}

}
